%%{
	machine JSON;

# This file is used with the Ragel state machine compiler 
# (http://www.cs.queensu.ca/~thurston/ragel/) to generate JSONReader.java
	
	action breakOnEvent {
		if (!events.isEmpty()) {
			fhold;
			fbreak;
		}
	}
	action pushFalse {
		//pushFalse
		events.add(new JSONEvent(getSource(), EventType.Boolean, Boolean.FALSE, key));
	}
	
	action pushNull {
		//pushNull
		events.add(new JSONEvent(getSource(), EventType.Null, null, key));
	}

	action pushTrue {
		//pushTrue
		events.add(new JSONEvent(getSource(), EventType.Boolean, Boolean.TRUE, key));
	}
	
	action startKey {
		//startKey
		clearStringBuffer ();
		stringStart = fpc + 1; // after quote
	}
	action pushKey {
		//pushKey
		// capture into 'key' variable for use by code.
		sb.append(data, stringStart, fpc - stringStart );
		key = sb.toString();
	}
	action startString {
		//startString
		clearStringBuffer ();
		stringStart = fpc + 1; // after quote
	}
	action pushString {
		//pushString
		sb.append(data, stringStart, fpc - stringStart );
		events.add(new JSONEvent(getSource(), EventType.String, sb.toString(), key));
	}
	action report {
		//report
		// error, raise syntax exception
		if (true) throw new SyntaxException(p, cs);
	}

	action startNum {
		//startNum
		clearStringBuffer ();
		stringStart = fpc;
	}
 
	action pushNum {
		//pushNum
		// capture text encountered and store into BigDecimal
		// NOTE: it would be nice to put this into an appropriately sized,
		// integer vs. floating point type.
		sb.append ( data, stringStart, fpc - stringStart );
		events.add(new JSONEvent(getSource(), EventType.Number, new BigDecimal(sb.toString()), key));
	}

	action unescapeQuote {
		appendEscapedChar( '\"' );
	}
  
	action unescapeBackslash {
		appendEscapedChar( '\\' );
	}

	action unescapeSlash {
		appendEscapedChar( '/' );
	}

	action unescapeBackspace {
		appendEscapedChar( '\b' );
	}

	action unescapeFormfeed {
		appendEscapedChar( '\f' );
	}

	action unescapeNewline {
		appendEscapedChar( '\n' );
	}

    action unescapeCarriageReturn{
		appendEscapedChar( '\r' );
	}

	action unescapeTab {
		appendEscapedChar( '\t' );
	}
  
	action unescapeUnicode {
		// back before \ u x x x x
		sb.append ( data, stringStart, p - stringStart - 5 );
		String point = new String ( data, p - 3, 4 );
		int code = Integer.parseInt ( point, 16 );
		sb.append ( (char) code );
		stringStart = p + 1;
	}
  
	action startArray {
    	//startArray
    	events.add(new JSONEvent(getSource(), EventType.StartArray, null, key));
	}

	action startObject {
    	//startObject
    	events.add(new JSONEvent(getSource(), EventType.StartObject, null, key));
	}

	action endArray {
		//endArray
		events.add(new JSONEvent(getSource(), EventType.EndArray, null, null));
	}

	action endObject {
		//endObject
		events.add(new JSONEvent(getSource(), EventType.EndObject, null, null));
	}

	include "json.rl";

}%%
/* Note: this file is generated from JSONTextWalker.rl and json.rl. Changes
 * made here will be overwritten when this file is regenerated. */
package com.alkalinesolutions.json.core.impl;

import java.lang.reflect.UndeclaredThrowableException;
import java.math.BigDecimal;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.NoSuchElementException;
import java.util.Queue;

import com.alkalinesolutions.json.core.DepthExceededException;
import com.alkalinesolutions.json.core.JSONException;
import com.alkalinesolutions.json.core.LengthExceededException;
import com.alkalinesolutions.json.core.Messages;
import com.alkalinesolutions.json.core.SyntaxException;
import com.alkalinesolutions.json.core.impl.JSONEvent.EventType;

/**
 * The parser class for JSON text. Note that per <a
 * href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>, JSON text can only
 * be arrays and objects, and not data types like numbers, booleans, and
 * strings.
 * 
 * @author David Waite &lt;<a href='mailto:david@alkaline-solutions.com'>david@alkaline-solutions.com</a>&gt;
 * 
 */
public class JSONTextWalker implements Iterator<JSONEvent>, Iterable<JSONEvent> {

	/*
	 * stringStart and partialString are used to represent strings while
	 * parsing. Note that numbers are also parsed as strings.
	 */
	private int stringStart;
	private final StringBuilder sb;

	// type and data information for walking the JSON document
	private final Queue<JSONEvent> events;

	private int cs, p, pe, eof, top;
	private int[] stack;
	private char[] data;
	private int maxLength;
	private Object source;
	
	public JSONTextWalker(Object source) {
		if (source == null)
			throw new IllegalArgumentException("source"); //$NON-NLS-1$
                this.source = source;
		
		events = new LinkedList<JSONEvent>();
		sb = new StringBuilder();
		stack = new int[19];
		maxLength = -1;
	}

	private Object getSource() {
		return source;
	}
	/**
	 * Set the maximum stack depth. Default is 19 (19 nested levels, for 20
	 * total).
	 */
	public void setStackDepth(final int depth) {
		stack = new int[depth];
	}

	/** Get the maximum stack depth. */
	public int getStackDepth() {
		return stack.length;
	}

	/**
	 * Set the maximum length of JSON text to allow, in characters. Default is
	 * <code>-1</code>, which will allow any size JSON text to be parsed.
	 */
	public void setMaxLength(final int maxLength) {
		this.maxLength = maxLength;
	}

	/**
	 * Get the maximum length of JSON text allowed, in characters. The special
	 * value <code>-1</code> indicates any length is allowed.
	 */
	public int getMaxLength() {
		return maxLength;
	}

	private void clearStringBuffer() {
		sb.delete(0, sb.length());
	}

	private void appendEscapedChar(final char ch) {
		sb.append(data, stringStart, p - stringStart - 1);
		stringStart = p + 1;
		sb.append(ch);
	}

%% write data;

	/**
	 * Parse JSON text into either an array/list, or object (map).
	 * 
	 * @param value
	 *            structure text to parse
	 * @throws JSONException
	 *             on parsing errors
	 */
	public void loadText(final String value) throws JSONException {
		if ((value == null) || (value.length() == 0))
			throw new IllegalArgumentException("value"); //$NON-NLS-1$

		pe = value.length();
		if ((pe > maxLength) && (maxLength >= 0))
			throw new LengthExceededException(maxLength);

		data = value.toCharArray();
		eof = pe;

		events.clear();

		stringStart = cs = p = top = 0;
		// Generated Initialization Code
		%% write init;
	}

	public JSONEvent next() {
		try {
			if (p != pe && events.isEmpty())
				seek();
			return events.remove();
		} catch (final JSONException e) {
			throw new UndeclaredThrowableException(e);
		}
	}

	@SuppressWarnings( { "fallthrough", "cast" })
	private void seek() throws JSONException {
		String key = null;

		if (!events.isEmpty())
			return;

		if (p == eof)
			// Indexing past end of data
			throw new NoSuchElementException(Messages
					.getString("JSONCursor.past-end")); //$NON-NLS-1$
		try {
			// Generated Execution Code
			%% write exec;
		} catch (final ArrayIndexOutOfBoundsException e) {
			if (top >= stack.length)
				throw new DepthExceededException(p, top);
			throw e;
		}
	}

	public Iterator<JSONEvent> iterator() {
		return this;
	}

	public boolean hasNext() {
		if (!events.isEmpty()) return true;
		if (p == pe) return false;
		try {
			seek();
		}
		catch (Exception e) {
			throw new UndeclaredThrowableException(e);
		}
		return (!events.isEmpty());
	}

	public void remove() {
		throw new UnsupportedOperationException();
	}
}
